"""
投资组合管理器

提供投资组合跟踪、持仓管理、风险控制等功能。
"""

from abc import ABC, abstractmethod
from enum import Enum
from typing import Dict, List, Optional, Any, Callable, Set
from datetime import datetime, timedelta
from dataclasses import dataclass, field
import asyncio
from decimal import Decimal
import pandas as pd

from .order_manager import Order, OrderFill, OrderSide


class PositionType(Enum):
    """持仓类型枚举"""
    LONG = "long"  # 多头
    SHORT = "short"  # 空头
    FLAT = "flat"  # 平仓


class PositionStatus(Enum):
    """持仓状态枚举"""
    OPEN = "open"  # 开仓
    CLOSED = "closed"  # 平仓
    PARTIAL = "partial"  # 部分平仓


@dataclass
class Position:
    """持仓信息"""
    symbol: str  # 标的代码
    position_type: PositionType  # 持仓类型
    quantity: Decimal  # 持仓数量
    avg_price: Decimal  # 平均成本价
    market_price: Optional[Decimal] = None  # 市场价格
    unrealized_pnl: Decimal = Decimal('0')  # 浮动盈亏
    realized_pnl: Decimal = Decimal('0')  # 已实现盈亏
    total_cost: Decimal = Decimal('0')  # 总成本
    market_value: Decimal = Decimal('0')  # 市值
    status: PositionStatus = PositionStatus.OPEN  # 持仓状态
    strategy_id: Optional[str] = None  # 策略ID
    created_at: datetime = field(default_factory=datetime.now)
    updated_at: datetime = field(default_factory=datetime.now)
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    def update_market_price(self, price: Decimal) -> None:
        """更新市场价格"""
        self.market_price = price
        self.market_value = self.quantity * price
        
        if self.position_type == PositionType.LONG:
            self.unrealized_pnl = (price - self.avg_price) * self.quantity
        elif self.position_type == PositionType.SHORT:
            self.unrealized_pnl = (self.avg_price - price) * self.quantity
        
        self.updated_at = datetime.now()
    
    def get_total_pnl(self) -> Decimal:
        """获取总盈亏"""
        return self.realized_pnl + self.unrealized_pnl
    
    def get_pnl_percentage(self) -> float:
        """获取盈亏百分比"""
        if self.total_cost == 0:
            return 0.0
        return float(self.get_total_pnl() / self.total_cost * 100)


@dataclass
class Trade:
    """交易记录"""
    trade_id: str  # 交易ID
    symbol: str  # 标的代码
    side: OrderSide  # 交易方向
    quantity: Decimal  # 交易数量
    price: Decimal  # 交易价格
    commission: Decimal  # 手续费
    timestamp: datetime  # 交易时间
    order_id: str  # 订单ID
    strategy_id: Optional[str] = None  # 策略ID
    execution_id: Optional[str] = None  # 执行ID
    metadata: Dict[str, Any] = field(default_factory=dict)
    
    @property
    def trade_value(self) -> Decimal:
        """交易金额"""
        return self.quantity * self.price
    
    @property
    def net_value(self) -> Decimal:
        """净交易金额（扣除手续费）"""
        return self.trade_value - self.commission


@dataclass
class PortfolioSnapshot:
    """投资组合快照"""
    timestamp: datetime  # 快照时间
    total_value: Decimal  # 总价值
    cash: Decimal  # 现金
    positions_value: Decimal  # 持仓价值
    unrealized_pnl: Decimal  # 浮动盈亏
    realized_pnl: Decimal  # 已实现盈亏
    total_pnl: Decimal  # 总盈亏
    positions_count: int  # 持仓数量
    leverage: float = 1.0  # 杠杆率
    margin_used: Decimal = Decimal('0')  # 已用保证金
    margin_available: Decimal = Decimal('0')  # 可用保证金
    risk_metrics: Dict[str, float] = field(default_factory=dict)  # 风险指标


class Portfolio:
    """
    投资组合
    
    管理投资组合的持仓、现金、盈亏等信息。
    """
    
    def __init__(
        self,
        portfolio_id: str,
        initial_cash: Decimal = Decimal('1000000'),
        currency: str = "CNY"
    ):
        """
        初始化投资组合
        
        Args:
            portfolio_id: 投资组合ID
            initial_cash: 初始现金
            currency: 货币类型
        """
        self.portfolio_id = portfolio_id
        self.currency = currency
        self.cash = initial_cash
        self.initial_cash = initial_cash
        
        # 持仓管理
        self.positions: Dict[str, Position] = {}  # symbol -> Position
        self.trades: List[Trade] = []  # 交易记录
        self.snapshots: List[PortfolioSnapshot] = []  # 快照历史
        
        # 统计信息
        self.total_commission = Decimal('0')  # 总手续费
        self.total_realized_pnl = Decimal('0')  # 总已实现盈亏
        self.created_at = datetime.now()
        self.updated_at = datetime.now()
        
        # 回调函数
        self._callbacks: Dict[str, List[Callable]] = {
            "position_opened": [],
            "position_closed": [],
            "position_updated": [],
            "trade_executed": [],
            "portfolio_updated": []
        }
    
    def add_callback(self, event_type: str, callback: Callable) -> None:
        """添加回调函数"""
        if event_type in self._callbacks:
            self._callbacks[event_type].append(callback)
    
    def remove_callback(self, event_type: str, callback: Callable) -> None:
        """移除回调函数"""
        if event_type in self._callbacks and callback in self._callbacks[event_type]:
            self._callbacks[event_type].remove(callback)
    
    def _trigger_callback(self, event_type: str, *args) -> None:
        """触发回调函数"""
        if event_type in self._callbacks:
            for callback in self._callbacks[event_type]:
                try:
                    callback(*args)
                except Exception as e:
                    print(f"Portfolio callback error: {e}")
    
    def process_fill(self, fill: OrderFill) -> None:
        """
        处理订单成交
        
        Args:
            fill: 订单成交信息
        """
        # 创建交易记录
        trade = Trade(
            trade_id=f"trade_{fill.fill_id}",
            symbol=fill.symbol,
            side=fill.side,
            quantity=fill.quantity,
            price=fill.price,
            commission=fill.commission,
            timestamp=fill.timestamp,
            order_id=fill.order_id,
            strategy_id=getattr(fill, 'strategy_id', None),
            execution_id=getattr(fill, 'execution_id', None)
        )
        
        self.trades.append(trade)
        self.total_commission += fill.commission
        
        # 更新持仓
        self._update_position(trade)
        
        # 更新现金
        if fill.side == OrderSide.BUY:
            self.cash -= trade.trade_value + trade.commission
        else:  # SELL
            self.cash += trade.trade_value - trade.commission
        
        self.updated_at = datetime.now()
        
        # 触发回调
        self._trigger_callback("trade_executed", trade)
        self._trigger_callback("portfolio_updated", self)
    
    def _update_position(self, trade: Trade) -> None:
        """更新持仓信息"""
        symbol = trade.symbol
        
        if symbol not in self.positions:
            # 新开仓
            if trade.side == OrderSide.BUY:
                position_type = PositionType.LONG
            else:
                position_type = PositionType.SHORT
            
            position = Position(
                symbol=symbol,
                position_type=position_type,
                quantity=trade.quantity,
                avg_price=trade.price,
                total_cost=trade.trade_value,
                strategy_id=trade.strategy_id
            )
            
            self.positions[symbol] = position
            self._trigger_callback("position_opened", position)
        
        else:
            # 更新现有持仓
            position = self.positions[symbol]
            
            if trade.side == OrderSide.BUY:
                if position.position_type == PositionType.LONG:
                    # 加仓
                    total_cost = position.total_cost + trade.trade_value
                    total_quantity = position.quantity + trade.quantity
                    position.avg_price = total_cost / total_quantity
                    position.quantity = total_quantity
                    position.total_cost = total_cost
                
                elif position.position_type == PositionType.SHORT:
                    # 平空仓
                    if trade.quantity >= position.quantity:
                        # 完全平仓或反向开仓
                        realized_pnl = (position.avg_price - trade.price) * position.quantity
                        self.total_realized_pnl += realized_pnl
                        position.realized_pnl += realized_pnl
                        
                        remaining_quantity = trade.quantity - position.quantity
                        if remaining_quantity > 0:
                            # 反向开多仓
                            position.position_type = PositionType.LONG
                            position.quantity = remaining_quantity
                            position.avg_price = trade.price
                            position.total_cost = remaining_quantity * trade.price
                        else:
                            # 完全平仓
                            position.status = PositionStatus.CLOSED
                            position.quantity = Decimal('0')
                    else:
                        # 部分平仓
                        realized_pnl = (position.avg_price - trade.price) * trade.quantity
                        self.total_realized_pnl += realized_pnl
                        position.realized_pnl += realized_pnl
                        position.quantity -= trade.quantity
                        position.status = PositionStatus.PARTIAL
            
            else:  # SELL
                if position.position_type == PositionType.SHORT:
                    # 加空仓
                    total_cost = position.total_cost + trade.trade_value
                    total_quantity = position.quantity + trade.quantity
                    position.avg_price = total_cost / total_quantity
                    position.quantity = total_quantity
                    position.total_cost = total_cost
                
                elif position.position_type == PositionType.LONG:
                    # 平多仓
                    if trade.quantity >= position.quantity:
                        # 完全平仓或反向开仓
                        realized_pnl = (trade.price - position.avg_price) * position.quantity
                        self.total_realized_pnl += realized_pnl
                        position.realized_pnl += realized_pnl
                        
                        remaining_quantity = trade.quantity - position.quantity
                        if remaining_quantity > 0:
                            # 反向开空仓
                            position.position_type = PositionType.SHORT
                            position.quantity = remaining_quantity
                            position.avg_price = trade.price
                            position.total_cost = remaining_quantity * trade.price
                        else:
                            # 完全平仓
                            position.status = PositionStatus.CLOSED
                            position.quantity = Decimal('0')
                    else:
                        # 部分平仓
                        realized_pnl = (trade.price - position.avg_price) * trade.quantity
                        self.total_realized_pnl += realized_pnl
                        position.realized_pnl += realized_pnl
                        position.quantity -= trade.quantity
                        position.status = PositionStatus.PARTIAL
            
            position.updated_at = datetime.now()
            
            if position.status == PositionStatus.CLOSED:
                self._trigger_callback("position_closed", position)
            else:
                self._trigger_callback("position_updated", position)
    
    def update_market_prices(self, prices: Dict[str, Decimal]) -> None:
        """
        更新市场价格
        
        Args:
            prices: 价格字典 {symbol: price}
        """
        for symbol, price in prices.items():
            if symbol in self.positions:
                self.positions[symbol].update_market_price(price)
        
        self.updated_at = datetime.now()
        self._trigger_callback("portfolio_updated", self)
    
    def get_position(self, symbol: str) -> Optional[Position]:
        """获取持仓信息"""
        return self.positions.get(symbol)
    
    def get_active_positions(self) -> Dict[str, Position]:
        """获取活跃持仓"""
        return {
            symbol: position for symbol, position in self.positions.items()
            if position.status == PositionStatus.OPEN and position.quantity > 0
        }
    
    def get_total_value(self) -> Decimal:
        """获取总价值"""
        positions_value = sum(
            position.market_value for position in self.positions.values()
            if position.status == PositionStatus.OPEN
        )
        return self.cash + positions_value
    
    def get_unrealized_pnl(self) -> Decimal:
        """获取浮动盈亏"""
        total = Decimal('0')
        for position in self.positions.values():
            if position.status == PositionStatus.OPEN:
                total += position.unrealized_pnl
        return total
    
    def get_total_pnl(self) -> Decimal:
        """获取总盈亏"""
        return self.total_realized_pnl + self.get_unrealized_pnl()
    
    def get_return_rate(self) -> float:
        """获取收益率"""
        if self.initial_cash == 0:
            return 0.0
        return float(self.get_total_pnl() / self.initial_cash * 100)
    
    def create_snapshot(self) -> PortfolioSnapshot:
        """创建投资组合快照"""
        total_value = self.get_total_value()
        positions_value = total_value - self.cash
        unrealized_pnl = self.get_unrealized_pnl()
        total_pnl = self.get_total_pnl()
        
        snapshot = PortfolioSnapshot(
            timestamp=datetime.now(),
            total_value=total_value,
            cash=self.cash,
            positions_value=positions_value,
            unrealized_pnl=unrealized_pnl,
            realized_pnl=self.total_realized_pnl,
            total_pnl=total_pnl,
            positions_count=len(self.get_active_positions())
        )
        
        self.snapshots.append(snapshot)
        return snapshot
    
    def get_trades_by_symbol(self, symbol: str) -> List[Trade]:
        """获取指定标的的交易记录"""
        return [trade for trade in self.trades if trade.symbol == symbol]
    
    def get_trades_by_strategy(self, strategy_id: str) -> List[Trade]:
        """获取指定策略的交易记录"""
        return [trade for trade in self.trades if trade.strategy_id == strategy_id]
    
    def get_performance_metrics(self) -> Dict[str, Any]:
        """获取绩效指标"""
        if not self.trades:
            return {}
        
        # 基础指标
        total_value = self.get_total_value()
        total_pnl = self.get_total_pnl()
        return_rate = self.get_return_rate()
        
        # 交易统计
        total_trades = len(self.trades)
        winning_trades = len([t for t in self.trades if self._is_winning_trade(t)])
        win_rate = winning_trades / total_trades if total_trades > 0 else 0
        
        # 时间统计
        trading_days = (datetime.now() - self.created_at).days
        if trading_days == 0:
            trading_days = 1
        
        return {
            "total_value": float(total_value),
            "total_pnl": float(total_pnl),
            "return_rate": return_rate,
            "total_trades": total_trades,
            "winning_trades": winning_trades,
            "win_rate": win_rate,
            "total_commission": float(self.total_commission),
            "trading_days": trading_days,
            "avg_daily_return": return_rate / trading_days,
            "positions_count": len(self.get_active_positions()),
            "cash_ratio": float(self.cash / total_value) if total_value > 0 else 0
        }
    
    def _is_winning_trade(self, trade: Trade) -> bool:
        """判断是否为盈利交易"""
        # 简化判断，实际应该根据开平仓配对计算
        position = self.get_position(trade.symbol)
        if position:
            return position.get_total_pnl() > 0
        return False


class PortfolioManager:
    """
    投资组合管理器
    
    管理多个投资组合，提供统一的接口。
    """
    
    def __init__(self):
        """初始化投资组合管理器"""
        self.portfolios: Dict[str, Portfolio] = {}
        self._callbacks: Dict[str, List[Callable]] = {
            "portfolio_created": [],
            "portfolio_updated": [],
            "portfolio_deleted": []
        }
    
    def create_portfolio(
        self,
        portfolio_id: str,
        initial_cash: Decimal = Decimal('1000000'),
        currency: str = "CNY"
    ) -> Portfolio:
        """
        创建投资组合
        
        Args:
            portfolio_id: 投资组合ID
            initial_cash: 初始现金
            currency: 货币类型
            
        Returns:
            Portfolio: 投资组合对象
        """
        if portfolio_id in self.portfolios:
            raise ValueError(f"Portfolio {portfolio_id} already exists")
        
        portfolio = Portfolio(portfolio_id, initial_cash, currency)
        self.portfolios[portfolio_id] = portfolio
        
        # 添加组合更新回调
        portfolio.add_callback("portfolio_updated", 
                             lambda p: self._trigger_callback("portfolio_updated", p))
        
        self._trigger_callback("portfolio_created", portfolio)
        return portfolio
    
    def get_portfolio(self, portfolio_id: str) -> Optional[Portfolio]:
        """获取投资组合"""
        return self.portfolios.get(portfolio_id)
    
    def delete_portfolio(self, portfolio_id: str) -> bool:
        """删除投资组合"""
        if portfolio_id in self.portfolios:
            portfolio = self.portfolios[portfolio_id]
            del self.portfolios[portfolio_id]
            self._trigger_callback("portfolio_deleted", portfolio)
            return True
        return False
    
    def get_all_portfolios(self) -> Dict[str, Portfolio]:
        """获取所有投资组合"""
        return self.portfolios.copy()
    
    def process_fill(self, portfolio_id: str, fill: OrderFill) -> bool:
        """
        处理订单成交
        
        Args:
            portfolio_id: 投资组合ID
            fill: 订单成交信息
            
        Returns:
            bool: 处理是否成功
        """
        portfolio = self.get_portfolio(portfolio_id)
        if portfolio:
            portfolio.process_fill(fill)
            return True
        return False
    
    def update_market_prices(self, prices: Dict[str, Decimal]) -> None:
        """更新所有投资组合的市场价格"""
        for portfolio in self.portfolios.values():
            portfolio.update_market_prices(prices)
    
    def get_aggregated_positions(self) -> Dict[str, Decimal]:
        """获取聚合持仓"""
        aggregated = {}
        for portfolio in self.portfolios.values():
            for symbol, position in portfolio.get_active_positions().items():
                if symbol not in aggregated:
                    aggregated[symbol] = Decimal('0')
                
                if position.position_type == PositionType.LONG:
                    aggregated[symbol] += position.quantity
                else:
                    aggregated[symbol] -= position.quantity
        
        return aggregated
    
    def get_total_value(self) -> Decimal:
        """获取所有投资组合的总价值"""
        total = Decimal('0')
        for portfolio in self.portfolios.values():
            total += portfolio.get_total_value()
        return total
    
    def get_total_pnl(self) -> Decimal:
        """获取所有投资组合的总盈亏"""
        total = Decimal('0')
        for portfolio in self.portfolios.values():
            total += portfolio.get_total_pnl()
        return total
    
    def add_callback(self, event_type: str, callback: Callable) -> None:
        """添加回调函数"""
        if event_type in self._callbacks:
            self._callbacks[event_type].append(callback)
    
    def remove_callback(self, event_type: str, callback: Callable) -> None:
        """移除回调函数"""
        if event_type in self._callbacks and callback in self._callbacks[event_type]:
            self._callbacks[event_type].remove(callback)
    
    def _trigger_callback(self, event_type: str, *args) -> None:
        """触发回调函数"""
        if event_type in self._callbacks:
            for callback in self._callbacks[event_type]:
                try:
                    callback(*args)
                except Exception as e:
                    print(f"PortfolioManager callback error: {e}")
    
    def get_summary_statistics(self) -> Dict[str, Any]:
        """获取汇总统计信息"""
        total_portfolios = len(self.portfolios)
        total_value = self.get_total_value()
        total_pnl = self.get_total_pnl()
        
        portfolio_stats = []
        for portfolio in self.portfolios.values():
            stats = portfolio.get_performance_metrics()
            stats["portfolio_id"] = portfolio.portfolio_id
            portfolio_stats.append(stats)
        
        return {
            "total_portfolios": total_portfolios,
            "total_value": float(total_value),
            "total_pnl": float(total_pnl),
            "portfolio_stats": portfolio_stats,
            "aggregated_positions": {
                symbol: float(quantity) 
                for symbol, quantity in self.get_aggregated_positions().items()
            }
        }